/*
 * Copyright 2002-2018 the original author or authors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      https://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.springframework.security.web.context.request.async;

import java.util.concurrent.Callable;

import org.springframework.security.core.context.SecurityContext;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.util.Assert;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.context.request.async.CallableProcessingInterceptor;
import org.springframework.web.context.request.async.CallableProcessingInterceptorAdapter;

/**
 * 允许与 Spring MVC 的 {@link Callable} 支持集成。
 *
 * SecurityContextCallableProcessingInterceptor 。它实现了接口 CallableProcessingInterceptor，
 * 当它被应用于一次异步执行时，beforeConcurrentHandling() 方法会在调用者线程执行，
 * 该方法会相应地从当前线程获取 SecurityContext，然后被调用者线程中执行逻辑时，会使用这个 SecurityContext，从而实现安全上下文从调用者线程到被调用者线程的传输。
 *
 * WebAsyncManagerIntegrationFilter 通过 WebSecurityConfigurerAdapter#getHttp()方法添加到 HttpSecurity 中成为 DefaultSecurityFilterChain 的一个链节。

 *
 */
public final class SecurityContextCallableProcessingInterceptor extends CallableProcessingInterceptorAdapter {

	private volatile SecurityContext securityContext;

	public SecurityContextCallableProcessingInterceptor() {
	}

	public SecurityContextCallableProcessingInterceptor(SecurityContext securityContext) {
		Assert.notNull(securityContext, "securityContext cannot be null");
		setSecurityContext(securityContext);
	}

	/**
	 * beforeConcurrentHandling() 方法会在调用者线程执行，该方法会相应地从当前线程获取 SecurityContext，
	 * 然后被调用者线程中执行逻辑时，会使用这个 SecurityContext，从而实现安全上下文从调用者线程到被调用者线程的传输。
	 */
	@Override
	public <T> void beforeConcurrentHandling(NativeWebRequest request, Callable<T> task) {
		if (this.securityContext == null) {
			setSecurityContext(SecurityContextHolder.getContext());
		}
	}

	/**
	 * 预处理：往 SecurityContextHolder 注入 securityContext
	 */
	@Override
	public <T> void preProcess(NativeWebRequest request, Callable<T> task) {
		SecurityContextHolder.setContext(this.securityContext);
	}

	/**
	 * 后置处理：清理 SecurityContextHolder 中的 securityContext
	 */
	@Override
	public <T> void postProcess(NativeWebRequest request, Callable<T> task, Object concurrentResult) {
		SecurityContextHolder.clearContext();
	}

	private void setSecurityContext(SecurityContext securityContext) {
		this.securityContext = securityContext;
	}

}
